import random
import time
from datetime import datetime
from threading import Lock


# TODO：协调者先发起询问，参与者接到询问后，将自己的事务操作写入日志，然后执行事务，执行成功则返回同意提交，失败则返回不同意
#   协调者根据相应的结果回复提交或回滚，参与者根据协调者的要求进行提交或回滚操作，并反馈
class Participant:
    def __init__(self, participant_id, coordinator):
        self.coordinator = coordinator
        self.participant_id = participant_id  # 参与者id,主线程创建该参与者时提供
        self.participant_name = f'participant_{participant_id}'  # 参与者名称,主线程创建该参与者时提供
        self.response_time = datetime.now()  # 响应时间
        self.what_phase = -1  # 哪个阶段，同步协调者阶段的参数，0->投票阶段；1->提交阶段；默认为-1：无操作阶段；
        self.action = -1  # 最终操作，同步协调者操作指令的参数，0->提交；1->回滚；默认为-1：初始无操作状态
        self.lock = coordinator.lock  # 绑定同一个资源互斥锁

    def start(self):
        while 1:
            self.wait_voting()  # 等待投票阶段
            self.wait_commit()  # 等待提交阶段
            self.what_phase = -1  # 一次二段提交完成，重置标志量
            time.sleep(2)

    # 等待投票阶段
    def wait_voting(self):
        while 1:
            if self.what_phase == 0:
                # 先写日志，再执行事务，根据执行成功或失败回复协调者同意或不同意
                self.response_time = datetime.now()
                with self.lock:  # 1.写日志
                    f = open('./log.txt', 'a', encoding='utf-8')
                    f.write(f'\n参与者{self.participant_id}:{self.response_time},test')
                    f.close()
                    # 2.执行事务
                    # TODO:此处为模拟执行事务的代码，
                    #   以下只是模拟出一个事务执行的结果可能成功也可能失败，且失败的可能性较低
                    success = False
                    # TODO:通过两次随机将事务执行失败的可能性降低
                    random_num = random.randint(5, 25)  # 1.先取一个随机值作为模拟判定成功的依据
                    if random.randint(0, 30) != random_num:  # 2.如果两次随机的结果一致则失败
                        success = True
                    # 3.根据执行是否成功，进行投票回复
                    # TODO：此行为将自己的投票情况添加进协调者的投票字典中的代码
                    self.coordinator.voting_dict[self.participant_name] = success
                    self.response_time = datetime.now()
                    if success:
                        print(f'参与者{self.participant_id}:因事务执行 成功，已于{self.response_time} 同意 提交操作，正在等待下一步指令！')
                    else:
                        print(f'参与者{self.participant_id}:因事务执行 失败，已于{self.response_time} 拒绝 提交操作，正在等待下一步指令！')
                    self.coordinator.response_num += 1  # 为协调者响应数加1
                    return

    # 等待提交阶段
    def wait_commit(self):
        while 1:
            if self.what_phase == 1:
                # 根据协调者的提交阶段指令，提交或回滚
                if self.action == 0:
                    # 提交操作
                    print(f'参与者{self.participant_id}:根据协调者指令，已于{self.response_time} 完成了提交操作！')
                elif self.action == 1:
                    # 回滚操作
                    print(f'参与者{self.participant_id}:根据协调者指令，已于{self.response_time} 完成了回滚操作！')
                self.coordinator.response_num -= 1  # 为协调者响应数减1
                self.action = -1
                return


class Coordinator:
    def __init__(self):
        self.thread_list = []  # 工作线程列表，初始最后一个为协调者
        self.participant_list = []
        self.voting_dict = {}  # 投票阶段响应字典,键为协调者名称，值为True(同意)或False(不同意)，
        self.commit_dict = {}  # 提交阶段响应字典，键值对说明同上
        self.sent_voting = False  # 是否进入已发送投票阶段询问
        self.sent_commit = False  # 是否进入已发送提交阶段回复
        self.response_num = 0  # 响应数，记录参与者已回复的个数，直到收到所有回复才进行下一步操作
        self.action_time = datetime.now()  # 某一操作的时间
        self.lock = Lock()  # 临界资源访问的锁

    def start(self):
        while 1:
            if not self.sent_voting:  # 1.投票
                self.sent_voting = True
                self.send_voting()  # 发起投票阶段
                time.sleep(1)
            if self.sent_voting and not self.sent_commit:  # 2.提交
                self.sent_commit = True
                self.send_commit()  # 发起提交阶段
                time.sleep(1)
            if self.sent_voting and self.sent_commit and self.response_num == 0:  # 3.收到所有回复后初始化
                print('协调者:已收到所有参与者回复！')
                self.voting_dict = {}
                self.commit_dict = {}
                self.sent_commit = False
                self.sent_voting = False
                self.response_num = 0
                time.sleep(1)
            time.sleep(2)

    # 投票阶段
    def send_voting(self):
        # 开始投票阶段
        self.action_time = datetime.now()
        print(f'协调者:{self.action_time} 即将开始投票阶段:\n')
        for participant in self.participant_list:
            participant.what_phase = 0  # 修改每个参与者阶段标志为0->投票阶段
        return

    # 提交阶段
    def send_commit(self):
        wait_start_time = datetime.now()  # 发起投票后的等待开始时间，防止作为判定响应超时的依据
        while 1:
            # print(self.response_num)
            wait_time = (datetime.now() - wait_start_time).total_seconds()  # 判定是否超时
            if self.response_num == len(self.participant_list) or wait_time > 20:  # 已收到全部投票回复或超时
                # 统计投票结果
                action = 0  # 默认为提交操作
                if wait_time > 20:  # 1.超时
                    action = 1  # 回滚指令
                else:  # 2.收到全部回复，逐一甄别
                    for (participant, value) in self.voting_dict.items():
                        if not value:  # 如果有参与者不同意则发起事务回滚操作
                            action = 1  # 回滚指令
                            break
                # 开始提交阶段
                self.action_time = datetime.now()
                print(f'协调者:{self.action_time} 已收到所有投票结果或投票阶段超时，即将开始提交阶段:\n')
                with self.lock:
                    for participant in self.participant_list:
                        participant.what_phase = 1  # 修改每个参与者阶段标志为1->提交阶段
                        participant.action = action  # 更新其操作指令
                return
            time.sleep(1)
